use elasticsearch::cat::CatIndicesParts;
use elasticsearch::http::transport::Transport;
use elasticsearch::{DeleteParts, Elasticsearch, IndexParts, SearchParts, UpdateParts};
use serde_json::{json, Value};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = Elasticsearch::new(Transport::single_node("http://172.18.2.249:9200").unwrap());
    // cat_indices(&client).await?;
    // delete_data(&client).await?;
    // update_data(&client).await?;

    search_data(&client).await?;

    Ok(())
}

#[allow(unused)]
async fn cat_indices(client: &Elasticsearch) -> Result<(), Box<dyn std::error::Error>> {
    let response = client
        .cat()
        .indices(CatIndicesParts::Index(&["*"]))
        .send()
        .await?;

    let res = response.text().await?;
    println!("res = {res}");

    let response = client
        .search(SearchParts::Index(&["cars"]))
        .body(json!({
            "query": {
                "bool": {
                    "must": [
                        {
                            "term": {
                                "brand": "大众"
                            },
                        },
                        {
                            "term": {
                                "color": "金色"
                            },
                        }
                    ]
                },
            }
        }))
        .send()
        .await?;

    let successful = response.status_code().is_success();
    println!("successful = {successful}");

    let value = response.json::<Value>().await?;

    Ok(())
}

#[allow(unused)]
async fn insert_data(client: &Elasticsearch) -> Result<(), Box<dyn std::error::Error>> {
    let response = client
        .index(IndexParts::IndexId("tst", "5"))
        .body(json!({
            "name": "哈皮",
            "age": 30,
            "birthday": "1993-07-01",
            "desc": "哈皮是一个舔狗"
        }))
        .send()
        .await?;

    let successful = response.status_code().is_success();
    println!("successful = {successful}");

    let res_str = response.text().await?;
    println!("res_str = {res_str}");
    Ok(())
}

#[allow(unused)]
async fn delete_data(client: &Elasticsearch) -> Result<(), Box<dyn std::error::Error>> {
    let response = client
        .delete(DeleteParts::IndexId("tst", "4"))
        .send()
        .await?;
    let response = response.error_for_status_code()?;
    println!("response = {:?}", response.text().await?);

    Ok(())
}

#[allow(unused)]
async fn update_data(client: &Elasticsearch) -> Result<(), Box<dyn std::error::Error>> {
    let response = client
        .update(UpdateParts::IndexId("tst", "5"))
        .if_seq_no(10)
        .if_primary_term(1)
        .body(json!({
            "doc": {
                "age": 32,
            }
        }))
        .send()
        .await?;

    let response = match response.error_for_status_code() {
        Ok(response) => response,
        Err(error) => {
            println!("update_data error = {error:?}");
            println!("update_data error = {error:#?}");
            return Err(error.into());
        }
    };

    let res_str = response.text().await?;
    println!("update_data res_str = {res_str}");

    Ok(())
}

#[allow(unused)]
async fn search_data(client: &Elasticsearch) -> Result<(), Box<dyn std::error::Error>> {
    let response = client
        .search(SearchParts::Index(&["tst"]))
        .body(json!({
            "query": {
                "match": {
                    "desc": "一个"
                }
            }
        }))
        .send()
        .await?;

    let response = response.error_for_status_code()?;

    let res_value = response.json::<Value>().await?;
    println!("res_value = {res_value}");

    let value = &res_value["hits"]["hits"];
    println!("value = {value}");
    let persons = serde_json::from_value::<Vec<EsHitResponse<Person>>>(value.clone()).unwrap();
    println!("persons = {persons:#?}");
    for p in persons.iter() {
        let person = &p._source;
        println!("person = {person:#?}");
    }
    Ok(())
}

#[derive(Debug, serde::Serialize, serde::Deserialize)]
struct Person {
    name: String,
    age: u8,
    birthday: String,
    desc: String,
}

#[derive(Debug, serde::Serialize, serde::Deserialize)]
struct EsHitResponse<T> {
    _index: String,
    _type: String,
    _id: String,
    _score: f64,
    _source: T,
}
